Hi!大家好啊!React
旅程走到這裡開始覺得有點累了XD,下班放假都還是不能忘記準備文章,嗚嗚...不過和不只報名一個系列的大大們來說,還算是算小咖了,希望這一次可以和大家一起走到最後,好的!題外話結束進入正文吧!
今天在準備文章的時候,發現為什麼有些範例寫的不同,再多查一下才發現原來React Router
經過了一次大改版「v4
」,在v4
版中的React Router
把所有的東西都包成了組件,也讓React Router
更接近React
的開發型態!所以本系列中選擇使用v4
版來學習,對v2
和v3
版就不太適用了,這點要注意一下!
掰惹位,因為小弟我沒有碰過v2
和v3
的版本,所以無法詳細舉出他們和v4
的不同,真的很不好意思!不過詳細差別可以參考下方文章:
React Router
登場!Router
就是路由!不過中文名稱不是重點XD,這個套件主要是用來搭配React
做SPA
的開發。
欸等等!這裡說的不是按摩的SPA,是大家常說的「單頁應用程式(single page web application)」,主要是因為React
的最小單位是組件,所以如果我們用它來開發SPA
的話,就不必每次都重新刷新頁面,只要在跳頁的時候用目標組件重新渲染畫面就可以了,其他和傳統Web
的差別上方的網址也講得很清楚!如果有興趣可以去看看。
首先把react-router-dom
裝上,在v4
版就不需要安裝react-router
才能使用這個,畢竟上面說他們的核心各自切出來了對吧!那我們先進行安裝了:
npm i react-router-dom
安裝完後就來建立專案吧!過程我就不說了,建好後假設我們有以下基本的目錄:
綠色框框內的Component內有幾個組件:
Title.jsx
:
import React from "react"
class Title extends React.Component {
render(){
return <h1>{this.props.title}</h1>
}
}
export {Title}
Home.jsx
:
import React from "react"
class Home extends React.Component{
render(){
return <p>這裡是首頁</p>
}
}
export {Home}
About.jsx
:
import React from "react"
class About extends React.Component{
render(){
return <p>這邊是關於我們</p>
}
}
export {About}
話說記得上面的那三隻組件都要記得在同目錄的index.js
做匯出哦!下面的Main.jsx
雖然我不會提,但也不要忘記哦!
Main.jsx
:
由於Main
組件會export
最後的結果,所以我把Router
寫在他上面,就像其他套件一樣,使用前要先import
物件進來,這裡除了React
外我們會需要Router
中的BrowserRouter
、Route
、Link
三個組件,所以寫成這樣:
import React from "react"
import { BrowserRouter, Route, Link } from "react-router-dom"
import { Title } from "../Title"
import { Home } from "../Home"
import { About } from "../About"
所有的Route
設定都要在BrowserRouter
組件中,所以先在render
中寫好他,另外要注意的是BrowserRouter
內只能有一個根DOM
,所以我們把所有內容寫在它的根div
中:
class Main extends React.Component {
render() {
return (
<BrowserRouter>
<div>
</div>
</BrowserRouter>
)
}
}
把Title
加上去,之後使用選單為每個Link
設定連結,這裡的連結不會導致重新刷新網站,只會在客戶端的網址後方加入to
中指定的值而已,等等我們可以來看一下:
class Main extends React.Component {
render() {
return (
<BrowserRouter>
<div>
<Title title="功能選單" />
<ul>
{/*Link組件中的to會改變網址,但不會刷新頁面*/}
<li><Link to="/">回到首頁</Link></li>
<li><Link to="/about">關於我們</Link></li>
</ul>
</div>
</BrowserRouter>
)
}
}
目前的畫面,當點擊關於我們時上方的網址會改變,但可以發現下方並沒有重新跳轉新的頁面,還是維持在原本的頁面:
最後用Route
組件來設定path
和component
的屬性,path
會去讀網址後方的路徑,並依渲染該Router
的component
所指定的組件:
class Main extends React.Component {
render() {
return (
<BrowserRouter>
<div>
<Title title="功能選單" />
<ul>
<li><Link to="/">回到首頁</Link></li>
<li><Link to="/about">關於我們</Link></li>
</ul>
{/*這邊塞一個分隔線*/}
<hr />
{/*路徑指定/代表根目錄,所以預設就會渲染Home組件,
而後方有/about的話會渲染About*/}
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
)
}
}
設定好後來看看結果:
運行的原理就是藉由Link
的to
去改變網址(上圖的1),之後Route
再判斷是否符合自身的path
去他的component
(上圖的2)!
但是!以為我會略過他嗎?Home
組件根本一直存在啊!他不是設定只會在根目錄出現嗎?就讓我來解釋吧!這裡的原因是就算我切換到/about
,也還是符合home
的Route
在path
指定的/
對吧?所以我們需要在Home
的Route
加入exact
進行嚴格比對!一定只有/
才顯示:
//在Route內加入exact就會進行嚴格比對path
<Route exact path="/" component={Home} />
這麼一來只有完全符合path
才會渲染出組件了:
所以如果沒有使用exact
的話,只要path
有前面符合就會渲染該組件,例如path="/Message"
的話,不論網址是http://localhost:8080/MessageList
或http://localhost:8080/MessageList/Content
都會當作符合而渲染組件。
就當我以為一天又平安過去的時候,猜猜看發生什麼事情?其實也沒什麼,不過就只是在/About
的時候點了重新整理:),但是讓我進入時空隧道的事情發生了,我看到了下方的畫面:
真的是一群老天鵝飛過心中對吧?怎麼會這樣子啊!上一秒還可以的欸!重新整理就掛掉太扯了吧!就在此時又開始查了一堆資料!總算是發現了原因,聽我娓娓道來吧!
由於網頁再換頁的時候都會發送一個GET
請求到伺服器上面,然後伺服器會回傳要顯示的畫面到客戶端上顯示,但是我們將Router
設定在客戶端,一開始會將所有的JavaScript
載入畫面中,只用一個index.html
頁面去渲染各個組件,而且剛剛我們上面有看,Link
的to
只會改變網址並不會有GET
的過程,所以在我們重新整理時,他依照網址/about
發送了一個GET
請求,不過我們並沒有叫做/about
這個目錄或是檔案對吧?因此就會出現錯誤。
不好意思我太激動了,但是到底該怎麼辦才好?
別急,就在官方的常見問題中也有記載,要處理這個狀況可以用HashRouter
取代BrowserRouter
:
記得上面的import
也要改:
import { HashRouter, Route, Link } from "react-router-dom"
之後是Main
組件的render()
:
class Main extends React.Component {
render() {
return (
<HashRouter>
<div>
<Title title="功能選單" />
<ul>
<li><Link to="/">回到首頁</Link></li>
<li><Link to="/about">關於我們</Link></li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</HashRouter>
)
}
}
設定好後會發現網址列多了一個#
,而接著不論怎麼重新整理都完全沒問題了:
網址後方的這個#
的意思是指,#
後的所有字串都不會發GET
請求到server
端,像是上方的http://localhost:8080/src/#/about
就還是只會送出http://localhost:8080/src/
到server
端,然後只要server
端回傳JavaScript
載入到客戶端,剩下的/about
就由Router
去判斷處理。
那既然這樣我們就使用HashRouter
就好了對吧!應該說在當你沒有其他選擇的狀況下,也只能選用HashRouter
,但如果開發的網站有上傳到伺服器管理(例如Appache
)的話,就能夠依照各個伺服器的設置來處理這個狀況,詳情可以看這篇文章今天小弟找到這篇的時候,是跪著看的。
最後附上今天的GitHub連結:
GitHub程式目錄連結
GitPage頁面連結
今天的文章到這裡,感謝各位大大的觀賞!如果文章中有任何錯誤或是講解不清楚的地方,都可以留言告訴我,小弟會盡快修改或補充文章內容的!謝謝大家
參考文章: